松本行弘的程序世界 13 关于数据的持久化
持久化数据的方法
保存文本
变换成文本的Marshal
将对象按一定的方式变换成文本,就可以保存到文件中去。这样的对象文本化就称为serialize(序列化),或是marshal(封送处理)。
使用Marshal模块
标准Ruby中,嵌入了marshal功能,这就是Marshal模块。
Marshal模块中提供了几乎能将全部Ruby对象变为字节串的方法dump,以及将字节串恢复成原对象(的复制)的load方法。
对象可以简单地保存到文件里。
复制有两种方式
使用Marshal可以完成对象的深复制。
复制对象的时候,通常使用clone方法。这种情况下,只复制直接对象,引用的对象不复制。称为浅复制。
深复制连同引用对象也一起进行递归复制。
仔细看Marshal的格式
Marshal用二进制形式将对象文本化。
不能保存的3类对象
Marshal在实现上有限制。以下3类对象不能保存:
- 定义了特异方法的对象。
- 输入、输出或是套接字等不能超越进程保存的对象。
- 在扩充库中定义,Ruby不知道保存方法的对象。
但是即使不能封送处理,若不是像输入输出那种从原理上不可能的情况,单纯是不知道封送处理方法的话,重新教一遍也就行了。
制作面向对象数据库
使用Marshal保存对象,使对象具有了持久性。所以,Marshal也可应用于面向对象数据库。
PStore库是Marshal的一个用例。Marshal虽然只是将数据变换成字符串,PStore却利用了这一点,简单地实现了面向对象数据库。
PStore有三个特征:使用Marshal,可以原封不动地保存任意的Ruby对象;具有容易使用的接口;有事务处理(transaction)。
PStore也有缺点,它不适合一下子将数据全部读入内存的大规模数据库。但几百字节的小规模数据库,应该没问题。
试用PStore
打开数据库
开始事务处理
对象的登录和取得
事务处理的终止
简单说明一下事务处理的步骤:
- 用flock将数据文件加锁。
- 用Marshal从数据文件中读取数据
- 执行(事务处理)块
- 块的执行成功,Marshal将数据写入数据文件
- 块的执行失败,什么也不做。
变换为文本的YAML
Marshal的变换结果是二进制文件,内容不容易看懂。有些场合及时效率低一些,也需要能够以更容易看懂的形式输出。能够满足要求的是YAML。使用文本形式,不依赖平台的体系结构,是一种对人而言易读易编辑的序列化格式。
有以下几个特征:记述简洁;结果容易读懂;使用缩进的层次表现;数据表现是专用的,不必烦恼标签的名称问题。
YMAL可以活用在Ruby on Rails的配置文件等各种各样的领域。YMAL是在Perl中开发的,但正式的支持,Ruby是第一个。
用YAML制作数据库
类似于PStore的东西,YAML::Store,其与PStore的互换性非常高,只要把名字换一换,面向PStore的程序在YAML::Store也能运行。
他俩的区别:
数据格式
很显然,一个YAML,一个Marshal。
数据量
Marshal比YAML紧凑的多,Marshal牺牲了易读性而实现了良好性能。
执行速度
性能优良不光是容量的问题。使用Marshal的PStore比YAML::Store速度高,在这一点上,也是数据量越大,两者的差异就越显著。
对象的保存
对象持久化库Madeleine,利用直接持久化对象的设计模式Object Prevalence。
Madeleine是Object Prevalence在Ruby中的实现,应称为PStore的发展形式。
PStore只是对象单纯由Marshal输出而来,Madeleine则与应用程序相协调,实现了高可靠性和高性能的持久化。
高速的Object Prevalence
所谓Prevalence,是一种实现应用程序持久化和进程间共享数据的设计模式。高性能的秘密在于直接访问内存中的数据。Object Prevalence将处理的数据保存在正在执行的应用程序的内存中,检索等操作不通过SQL而是直接进行,节省了与数据库副武器的通信成本,引用当然就会很高速。
但是,只有是同一进程,才能引用内存中的数据,进程一结束,数据马上消失。从持久化角度有必要解决这一问题。
Object Prevalence用日志记录(journaling)和快照(snapshot)来解决这一问题。Object Prevalence中,数据更新时不是直接更新对象,而是创建称为command的对象,采用的是一种非常间接的方法,在用command更新对象时,内存中的对象更新的同时,所有的更新内容也会写到称为日志(journal log)的外部文件中。
长此下去日志越来越大,所以要将现在数据状态写到称为快照的文件中。有了快照,老日志就不需要了,可在适当的时机删除。
有了最新的快照和最新的日志,可以完全恢复现在对象的状态。程序启动,按三步骤恢复内存的数据。及时有多个进程,只要写入日志的信息是完整的,就可以共享对象的状态。
- 如果不存在快照,就初始化应用程序数据。
- 如果存在快照,就读入其中最新的一个。
- 如果还存在日志,也将其读入,并用其中最新的一个更新应用程序数据。
Object Prevalence的问题点
Object Prevalence通过使用日志记录和快照实现了对象的持久化和进程间共享。Object Prevalence将所有数据都保存到内存中,随着数据量的增大,内存的消耗也在增大。
关系数据库中,不引用的数据放在文件中,必要的内存量就不用那么多了。
Object Prevalence有为了数据更新而具有的特殊结构,更新持久化数据时需要经由command对象。
使用Madeleine
访问时刻信息
让Madeleine更容易使用
Madeleine既保持简洁性,又能让对象持久化,但是最大的缺点是在每次更新应用程序时必须生成command对象。
Madeleine的实用例Instiki
Madeleine没有得到广泛应用,除了知道的人少,还因为数据全保存在内存中,就必须十分留意数据的大小。
Madeleine有一个很大的缺点,就是没有考虑多个进程同时更新数据的情况。
关于XML的考察
XML的祖先是SGML
SGML是将文档电子化的一种格式。由三部分组成:表示数据本身的Instance,表示数据结构的DTD,以及SGML声明。
由于SGML太复杂,处理成本太高,为了表现网页,将SGML特化为HTML,随之诞生的是XML。
XML不像HTML那样是为了特定目的的标记语言,它一开始就是为了通用目的而设计的。为了让XML在没有DTD来定义语法或提供schema信息的情况下,也能够解析,人们对其语法进行了简化。
XML是树结构的数据表现
XML基本上是纯文本,以类似于HTML的标签嵌套方式实现树结构。XML是继承了SGML的通用标记语言,其与SGML最大的区别是其基本语法固定,不依赖于DTD那样的外部信息也能解析。
即使没有标签的概要信息也能解析的语法称为良构的(well-formed),这是XML的一大特征。
优点在于纯文本
最大的优点在于XML基本上是纯文本的,表示结构的信息附加在标签里。
第二个优点是不易发生字符编码的问题。XML规定,在没有明确指定的情况下,字符编码均使用Unicode。
第三个优点是得益于良构的性质,在没有数据结构的情况下也能解析XML数据。这样就可以不考虑目的,而用共同的工具来处理XML数据。
第四个优点在于,XML与其解析工具不依赖于特定的语言,比如Java生成的XML数据在Ruby中的解析也很简单。解析XML的API,像DOM和SAX都超越语言提供了几乎共通的性质,所以不同语言也可以进行同样的操作。
最后一个有点是,人们也很容易理解。
总之,XML作为各种数据交换格式的框架,具有优良的性质。作为格式的格式,也就是元格式,是很优秀的。
缺点在于冗长
最大的缺点是效率低下。XML是以纯文本出现的,标签信息反复出现,显得冗长。与表示相同信息的二进制数据相比,XML数据的容量要大得多,与其他文本表现方式相比(YAML,JSON)也显得冗长。
效率低下不光体现在数据大小上,解析XML的效率也不怎么高。与二进制文件相比,XML文件的解析因为含有大量字符串处理,而容易变得很慢。
作为文本的标记语言而诞生的XML,用其表现有一定结构的数据到底好不好还是个疑问。如果只是用于表现构造数据,比XML更有效率的格式还有很多。而且XML原则上只能表现树结构的数据。
总结,XML作为出于通用目的的数据格式,效率很低,所谓很多优点,如果场合不对,也没多大意义。适才适用,XML也要分情况适当使用。
不适合重视效率的处理
对于重视通信量和速度的情况都不适合,此时应使用专用的协议或是效率更高的格式。
像配置文件那样靠人直接编辑的数据也不推荐XML。配置文件中,需要用到XML的树结构数据地方很少,随着要素数增加,就会很难读,用YMAL和JSON才更合适。
XML适合的场合:
- 人一般不直接接触
- 复杂性不成问题
- 效率不成为问题
- 跨平台
适合于信息交换的格式
利用XML的元格式性质,以XML为基础的格式的例子。
- RSS。Web网站更新信息。
- Atom。RSS的代替。
- ebXML。电子商务数据交换。
- SVG。向量-图像表示。
- SMIL。 多媒体及内容控制。
以上这些都具有XML的性质,可以用XML处理工具简单地解析。制作数据格式时,最麻烦的就是制作处理这种格式的软件。所以,XML与XML处理库的存在是很可贵的。
另外,XML数据库中,问题不在于数据是不是实际以纯文本XML来表现,而在于XML能够表现的树结构能够自由自在地操作。即,不是带标签的纯文本,而是由带属性、带内容的节点所构成的树结构本身才是最重要的。关系数据库的表只能表示间接数据,如果是树结构,可以直接操作直接表现的数据。
XML的解析
XML的解析方法有好几种。
DOM
DOM是文档对象模型的缩写,是对读取了XML数据的树结构进行操作的库。
SAX
Simple API for XML,与将数据全部读入内存的DOM不同,通常,SAX以数据流的形式读入XML,以事件驱动进行处理。SAX中,没必要将数据全部读入,这样往往处理效率更高,所以适合于将XML变换为其他形式的处理,反过来说,不适合于对树结构进行随机访问等用途。
XPath
XPath是用于指定XML树的一部分的书写格式。使用XPath,可以用节点名、属性名或是属性值等来选择特定的节点(群)。
XML处理库REXML
REXML是Ruby标准附属的XML处理库。REXML是具有DOM、SAX、SAX2以及XML Pull Parser等多照片那个功能的库。全部用Ruby实现,所以速度表现不怎么优秀。在特别重视效率的情况下,有必要用libxml等别的XML处理库。
XML的代替
JSON(JavaScript Object Notation)
JSON是把JavaScript的对象记法作为表现格式来使用。
将JSON数据原封不动地作为JavaScript去执行,就可以得到数据表现所对应的对象。但是JSON数据从外部读取的情况较多,实际上作为JavaScript直接执行容易引起安全上的问题,即使效率稍稍低一些,也应当使用解析JSON的库。
Ruby支持JSON。
YAML(YMAL ain’t Markup Language)
YAML是作为XML的对立面而诞生的,具有以下特征。完全放弃标记性记述,专注于数据表现;以缩进为基础表现数据结构;不要标签;可以对应各种语言。文件后缀为.yml
在用作数据表现及配置文件时,具有易读和不易变复杂等优点。实际上,YAML在Ruby on Rails中广泛用于配置文件。
另一方面,YAML到底是数据表现语言,没有相当于schema的东西,不适合于带结构的文本表现及元数据格式。
活用记号和缩进的YMAL比JSON更简洁,正如其名,YMAL不是标记语言,需要使用标记语言时还是XML合适。1
YAML是JSON的超集,采用空格来作为结构,JSON则是括号,一般YAML解析速度高于JSON,但对于某些东西定义的复杂性高于JSON,速度差别不大时可考虑用JSON。
Binary XML
与通常的XML有等价意义,但效率更高,采用二进制表现的是Binary XML。但现在还没有Binary XML的标准规格。
Protocol Buffer
Protocol Buffer使用一种“数据描述语言”来定义数据结构,然后从这个定义生成一个库,将原始数据变为二进制表现(序列化)。
1 | 持久数据的重要性 |